home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 52
/
Amiga Format AFCD52 (Issue 136, May 2000).iso
/
-serious-
/
cd-rom
/
showtoc
/
showtoc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-02-28
|
19KB
|
582 lines
/* :ts=3
** ShowTOC - shows Table Of Content of an AudioCD in a readable and
** freely configurable way
**
** written & © Ralph Reuchlein <amiga@rripley.de> 2000
**
** This software is eMailware, which means, that if you like this
** piece of software, you have to write me an eMail (address see end
** of readme).
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <clib/alib_protos.h>
#include <exec/memory.h>
#include <devices/scsidisk.h>
/* a 10 byte SCSI command */
typedef struct {
UBYTE opcode;
UBYTE b1;
UBYTE b2;
UBYTE b3;
UBYTE b4;
UBYTE b5;
UBYTE b6;
UBYTE b7;
UBYTE b8;
UBYTE control;
} SCSICMD10;
typedef struct {
UBYTE pad0;
UBYTE trackType;
UBYTE trackNum;
UBYTE pad1;
ULONG startFrame;
} TOCENTRY;
typedef struct {
UWORD length;
UBYTE firstTrack;
UBYTE lastTrack;
TOCENTRY tocs[100];
} TOC;
typedef struct {
UBYTE mins;
UBYTE secs;
UBYTE frames;
UBYTE fraction;
} TIMEINFO;
#define SCSI_CMD_READTOC 0x43
#define AUDIO_FRAMESIZE 2352
#define FRAMES_PER_SECOND 75
#define SECONDS_PER_MINUTE 60
#define SENSE_LEN 252
#define CDID_ARTIST_INDEX 0
#define CDID_TITLE_INDEX 1
#define CDID_TRACK_INDEX 2
const UBYTE ver_string[] = "$VER: ShowTOC 1.0 (08.01.00)Copyright © 2000 by Ralph Reuchlein";
/* ReadArgs defines ----------------------------------------------- */
#define ARGS_TEMPLATE "DEVICE=D/K/A,"\
"UNIT=U/N/K/A,"\
"FORMAT=FMT/K,"\
"INTRODUCER=INTR/K,"\
"CDIDDIR/K,"\
"HEADFORMAT=HEADFMT/K,"\
"TAILFORMAT=TAILFMT/K,"\
"NOHEADER/S,"\
"NOLIST/S,"\
"NOTAIL/S"
enum {
OPT_DEVICE = 0,
OPT_UNIT,
OPT_FMT,
OPT_INTR,
OPT_CDIDDIR,
OPT_HEADFMT,
OPT_TAILFMT,
OPT_NOHEAD,
OPT_NOLIST,
OPT_NOTAIL,
OPT_COUNT
};
static LONG args[OPT_COUNT];
#define ARG_DEVICE ( (STRPTR)args[OPT_DEVICE])
#define ARG_UNIT (*(ULONG *)args[OPT_UNIT])
#define ARG_FMT ( (STRPTR)args[OPT_FMT])
#define ARG_HEADFMT ( (STRPTR)args[OPT_HEADFMT])
#define ARG_TAILFMT ( (STRPTR)args[OPT_TAILFMT])
#define ARG_INTR ( (STRPTR)args[OPT_FMT])
#define ARG_CDIDDIR ( (STRPTR)args[OPT_CDIDDIR])
#define ARG_NOHEAD (args[OPT_NOHEAD]==-1)
#define ARG_NOLIST (args[OPT_NOLIST]==-1)
#define ARG_NOTAIL (args[OPT_NOTAIL]==-1)
#define IS_DEVICE (args[OPT_DEVICE])
#define IS_UNIT (args[OPT_UNIT])
#define IS_FMT (args[OPT_FMT])
#define IS_HEADFMT (args[OPT_HEADFMT])
#define IS_TAILFMT (args[OPT_TAILFMT])
#define IS_INTR (args[OPT_INTR])
#define IS_CDIDDIR (args[OPT_CDIDDIR])
#define IS_NOHEAD (args[OPT_NOHEAD]==-1)
#define IS_NOLIST (args[OPT_NOLIST]==-1)
#define IS_NOTAIL (args[OPT_NOTAIL]==-1)
#define DEFAULT_INTRODUCER '%'
#define DEFAULT_HEAD (STRPTR)" CD: %i%\\n Title: %T%\\nArtist(s): %a"
#define DEFAULT_FORMAT (STRPTR)"Track %2n: %d %t"
#define DEFAULT_TAIL (STRPTR)"CD length: %f"
#define DEFAULT_TIME_START_FORMAT (STRPTR)"%2sm:%02ss;%02sf"
#define DEFAULT_TIME_END_FORMAT (STRPTR)"%2em:%02es;%02ef"
#define DEFAULT_TIME_DURATION_FORMAT (STRPTR)"%2dm:%02ds;%02df"
#define DEFAULT_TIME_FULL_FORMAT (STRPTR)"%2fm:%02fs;%02ff"
#define CDID_FORMAT (STRPTR)"ID%02d%06X%06X"
/* Prototypes ----------------------------------------------------- */
void error(STRPTR,...);
TOC *scanTOC(STRPTR,UBYTE);
void getTimeInfo(TIMEINFO *,ULONG);
STRPTR *readCDID(STRPTR);
void freeCDID(STRPTR *);
void printFmt(STRPTR,TOC *,STRPTR *,UBYTE,BOOL);
static BYTE io_error;
#define BUF_SIZE 256
UBYTE buf[BUF_SIZE];
/* ---------------------------------------------------------------- */
void main(UWORD argc,STRPTR argv[]) {
struct RDArgs *rdargs;
TOC *toc;
STRPTR *cdids=NULL;
/* process arguments */
if (rdargs=ReadArgs(ARGS_TEMPLATE,args,NULL)) {
if ((toc=scanTOC(ARG_DEVICE,ARG_UNIT))) {
register int i;
UBYTE tracks=(toc->length/sizeof(TOCENTRY))-1;
if (IS_CDIDDIR) {
/* if CDIDDIR is specified, then build filename and try to
read in CDID data */
sprintf(buf,"%s",ARG_CDIDDIR);
AddPart(buf,"",BUF_SIZE);
sprintf(&buf[strlen(buf)],CDID_FORMAT,tracks,toc->tocs[2].startFrame,toc->tocs[tracks].startFrame);
cdids=readCDID(buf);
}
if (!IS_NOHEAD) printFmt((IS_HEADFMT)?(ARG_HEADFMT):(DEFAULT_HEAD),toc,cdids,0,TRUE);
if (!IS_NOLIST) for(i=0;i<tracks;i++) {
printFmt((IS_FMT)?(ARG_FMT):(DEFAULT_FORMAT),toc,cdids,i,TRUE);
}
if (!IS_NOTAIL) printFmt((IS_TAILFMT)?(ARG_TAILFMT):(DEFAULT_TAIL),toc,cdids,0,TRUE);
if (cdids) freeCDID(cdids);
FreeVec((APTR)toc);
}
else {
error("Error accessing %s, unit %i: error $%02x\n"
"(maybe no CD-ROM drive or no media?)\n",ARG_DEVICE,ARG_UNIT,io_error);
}
FreeArgs(rdargs);
}
else {
error("Usage: %s %s\n",argv[0],ARGS_TEMPLATE);
}
}
/* ---------------------------------------------------------------- */
void error(STRPTR fmt,...) {
va_list a;
va_start(a,fmt);
vfprintf(stdout,fmt,a);
va_end(a);
}
/* ---------------------------------------------------------------- */
TOC *scanTOC(STRPTR scsiname,UBYTE unit) {
static struct MsgPort *msgPort;
static struct IOStdReq *ioReq;
static struct SCSICmd scsicmd;
static SCSICMD10 *scsi;
static TOC *toc;
static UBYTE *sense;
BOOL ok=FALSE;
if ((scsi = (SCSICMD10 *)AllocVec(sizeof(SCSICMD10),MEMF_CLEAR|MEMF_CHIP))) {
if ((toc = (TOC *)AllocVec(sizeof(TOC),MEMF_CLEAR|MEMF_CHIP))) {
if ((sense = (UBYTE *)AllocVec(SENSE_LEN,MEMF_CLEAR|MEMF_CHIP))) {
/* create MsgPort */
if ((msgPort=CreatePort(NULL,0))!=NULL) {
/* create IOStdReq */
if ((ioReq=CreateStdIO(msgPort))!=NULL) {
/* open the SCSI device */
if (!OpenDevice(scsiname,unit,(struct IORequest *)ioReq,0)) {
/* fill IOStdReq */
ioReq->io_Length = sizeof(struct SCSICmd);
ioReq->io_Data = (APTR)&scsicmd;
ioReq->io_Command = HD_SCSICMD;
/* fill SCSICmd */
scsicmd.scsi_Data = (APTR)toc;
scsicmd.scsi_Length = sizeof(TOC);
scsicmd.scsi_Command = (APTR)scsi;
scsicmd.scsi_CmdLength = sizeof(SCSICMD10);
scsicmd.scsi_Flags = (SCSIF_READ | SCSIF_AUTOSENSE);
scsicmd.scsi_SenseData = (APTR)sense;
scsicmd.scsi_SenseLength = SENSE_LEN;
scsicmd.scsi_SenseActual = 0;
/* fill scsi */
scsi->opcode = SCSI_CMD_READTOC;
scsi->b1 = 0;
scsi->b2 = 0;
scsi->b3 = 0;
scsi->b4 = 0;
scsi->b5 = 0;
scsi->b6 = 0;
scsi->b7 = (sizeof(TOC)>>8);
scsi->b8 = (sizeof(TOC)&0xFF);
scsi->control = 0;
/* do the request */
if (DoIO((struct IORequest *)ioReq)==0) {;
ok=TRUE;
}
io_error = ioReq->io_Error;
/* close device */
CloseDevice((struct IORequest *)ioReq);
/* and remove all needed resources */
}
DeleteExtIO((struct IORequest *)ioReq);
}
DeletePort(msgPort);
}
FreeVec((APTR)sense);
}
/* do not free TOC, because we need the data */
}
FreeVec((APTR)scsi);
}
if (ok) {
return toc;
}
else {
FreeVec((APTR)toc);
}
return NULL;
}
/* ---------------------------------------------------------------- */
void getTimeInfo(TIMEINFO *ti,ULONG frames) {
ti->frames = frames % FRAMES_PER_SECOND;
ti->fraction = (UBYTE)(100 * (double)ti->frames / (double)FRAMES_PER_SECOND + 0.5);
frames /= FRAMES_PER_SECOND;
ti->secs = (UBYTE)(frames % SECONDS_PER_MINUTE);
frames /= SECONDS_PER_MINUTE;
ti->mins = (UBYTE) frames;
}
/* ---------------------------------------------------------------- */
STRPTR *readCDID(STRPTR filename) {
ULONG filelen=-1;
FILE *fh;
STRPTR cdid;
STRPTR *cdids=NULL;
/* try to examine file length */
struct FileInfoBlock *fib=AllocDosObject(DOS_FIB,NULL);
if (fib) {
BPTR l;
/* lock the file */
if (l=Lock(filename,ACCESS_READ)) {
/* examine file */
if (Examine(l,fib)) {
filelen=fib->fib_Size;
}
else {
error("%s: Couldn't examine file information!\n",filename);
}
UnLock(l);
}
else {
/*error("%s: Couldn't access to this file (maybe not found?)!\n",filename);*/
}
FreeDosObject(DOS_FIB,fib);
}
else {
error("%s: Couldn't allocate FileInfoBlock memory!\n",filename);
}
/* file length couldn't be examined (file not found etc.), return */
if (filelen==-1) return NULL;
/* read in CDID file and store line pointers */
if (fh=fopen(filename,"r")) {
if (cdid=AllocVec(filelen+1,MEMF_CLEAR|MEMF_REVERSE)) {
if (fread(cdid,1,filelen,fh)==filelen) {
register STRPTR cdidtmp=cdid;
register UWORD lines=0;
cdid[filelen]=0; /* terminate string */
/* scan the buffer for newlines */
while (*cdidtmp) {
if (*(cdidtmp++)=='\n') lines++;
}
/* allocate pointer list */
if (cdids=(STRPTR *)AllocVec((lines+1)*sizeof(STRPTR),MEMF_CLEAR|MEMF_REVERSE)) {
/* find line starts and store their pointers */
cdids[0]=cdid;
cdidtmp=cdid; lines=1;
while (*cdidtmp) {
if (*cdidtmp=='\n') {
*cdidtmp=0;
cdids[lines++]=(cdidtmp+1);
}
cdidtmp++;
}
cdids[lines]=NULL;
/* cdids not freed! */
}
else {
error("Couldn't allocate %i bytes for CDID pointer data.\n",(lines+1)*sizeof(STRPTR));
}
}
else {
error("%s: Reading error on CDID file!\n",filename);
}
/* cdid not freed! */
}
else {
error("Couldn't allocate %i bytes for CDID data.\n",filelen+1);
}
fclose(fh);
}
else {
error("%s: Couldn't open CDID file for reading!\n",filename);
}
return cdids;
}
void freeCDID(STRPTR *cdids) {
if (cdids) {
FreeVec((APTR)cdids[0]);
FreeVec((APTR)cdids);
}
}
/* ---------------------------------------------------------------- */
#define FMT_STRING 's'
#define FMT_INTEGER 'i'
void printFmt(STRPTR fmt,TOC *toc,STRPTR *cdids,UBYTE trackNum,BOOL newLine) {
register int tmp0,tmp1,intr;
STRPTR fmt_end = fmt + strlen(fmt),tmp_fmt,str2;
TIMEINFO ti;
ULONG frames;
UBYTE tracks=(toc->length/sizeof(TOCENTRY))-1;
/* if there is no format, return */
if (!fmt) return;
/* if toc is too small, return */
if (toc->length <= 6) return;
/* do we have specified a special introducer? */
if (ARG_INTR) {
intr = *ARG_INTR; /* only the first character */
}
else {
intr = DEFAULT_INTRODUCER;
}
/* we process the format string: replace the format types by
C-format types and process the format portion one by one */
tmp_fmt=fmt;
while(fmt<fmt_end) {
tmp_fmt=fmt;
/* search for the introducer */
while ((*fmt!=intr) && (fmt<fmt_end)) fmt++; if (fmt>=fmt_end) break;
/* write string til introducer */
*fmt=0; fputs(tmp_fmt,stdout); *fmt='%'; /* fprintf() only uses '%'! */
tmp_fmt=fmt;
/* search for the terminating format type character */
while ((*fmt!='\\') && (!isalpha(*fmt)) && (fmt<fmt_end)) fmt++; if (fmt>=fmt_end) break;
tmp0 = *fmt; tmp1=*(fmt+1);
switch (tmp0) {
case '\\': /* special characters */
switch (tmp1) {
case 'n': fputs("\n",stdout); break;
case 'r': fputs("\r",stdout); break;
case 'f': fputs("\f",stdout); break;
case 't': fputs("\t",stdout); break;
}
fmt+=2;
break;
case 's': /* start time of track */
case 'e': /* end time of track */
case 'd': /* duration of track */
case 'f': /* full CD time */
switch (tmp0) {
case 's':
frames=toc->tocs[trackNum].startFrame;
str2=DEFAULT_TIME_START_FORMAT;
break;
case 'e':
frames=toc->tocs[trackNum+1].startFrame;
str2=DEFAULT_TIME_END_FORMAT;
break;
case 'd':
frames=toc->tocs[trackNum+1].startFrame-toc->tocs[trackNum].startFrame;
str2=DEFAULT_TIME_DURATION_FORMAT;
break;
case 'f':
frames=toc->tocs[tracks].startFrame;
str2=DEFAULT_TIME_FULL_FORMAT;
break;
default:
frames=0; str2=""; /* should never be reached! */
}
getTimeInfo(&ti,frames);
switch(tmp1) {
case 'm':
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,ti.mins);
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
case 's':
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,ti.secs);
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
case 'f':
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,ti.frames);
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
case 'd':
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,ti.fraction);
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
case 'a':
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,frames);
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
case 'n':
if ((tmp0=='s') || (tmp0=='e')) {
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(tmp0=='s')?(toc->firstTrack):(toc->lastTrack));
*fmt=tmp0; fmt++; *fmt=tmp1; fmt++;
break;
}
default:
printFmt(str2,toc,cdids,trackNum,FALSE);
fmt++;
}
break;
case 'n': /* track number */
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,toc->tocs[trackNum].trackNum);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'N': /* number of tracks */
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,tracks);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'b': /* track length in bytes */
*fmt=FMT_INTEGER; *(fmt+1)=0;
frames=toc->tocs[trackNum+1].startFrame-toc->tocs[trackNum].startFrame;
fprintf(stdout,tmp_fmt,frames*AUDIO_FRAMESIZE);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'y': /* type of track */
*fmt=FMT_STRING; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(toc->tocs[trackNum].trackType & 0x04) ? "Data" : "Audio");
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'c': /* number of channels in track as numeric value */
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(toc->tocs[trackNum].trackType & 0x08) ? 4 : 2);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'C': /* number of channels in track as text */
*fmt=FMT_STRING; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(toc->tocs[trackNum].trackType & 0x08) ? "four" : "two");
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'p': /* Is digital copy of track permitted or prohibited? */
*fmt=FMT_STRING; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(toc->tocs[trackNum].trackType & 0x02) ? "permitted" : "prohibited");
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'P': /* Is digital copy of track permitted or prohibited? */
*fmt=FMT_STRING; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,(toc->tocs[trackNum].trackType & 0x02) ? "y" : "n");
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'i': /* CD ID */
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,CDID_FORMAT,tracks,toc->tocs[2].startFrame,toc->tocs[tracks].startFrame);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'l': /* TOC length on CD */
*fmt=FMT_INTEGER; *(fmt+1)=0;
fprintf(stdout,tmp_fmt,toc->length);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'T': /* Title of CD (only if CDID file is found/provided) */
*fmt=FMT_STRING; *(fmt+1)=0;
if (cdids) fprintf(stdout,tmp_fmt,cdids[CDID_TITLE_INDEX]);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 't': /* Title of track (only if CDID file is found/provided) */
*fmt=FMT_STRING; *(fmt+1)=0;
if (cdids) fprintf(stdout,tmp_fmt,cdids[CDID_TRACK_INDEX+trackNum]);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
case 'a': /* Artist of CD (only if CDID file is found/provided) */
*fmt=FMT_STRING; *(fmt+1)=0;
if (cdids) fprintf(stdout,tmp_fmt,cdids[CDID_ARTIST_INDEX]);
*fmt=tmp0; fmt++; *fmt=tmp1;
break;
default:
*(fmt+1)=0;
fputs(tmp_fmt,stdout);
fmt++; *fmt=tmp1;
}
tmp_fmt=fmt;
}
if (tmp_fmt<fmt) {
fputs(tmp_fmt,stdout);
}
if (newLine) fputc('\n',stdout);
}